package redislock

import (
	"codeup.aliyun.com/5f9118049cffa29cfdd3be1c/util"
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 续期脚本
const durationScriptLua = `
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("pexpire", KEYS[1], ARGV[2])
else
    return 0
end`

var durationScript = redis.NewScript(durationScriptLua)

// Holder 锁持有者
type Holder struct {
	// key 锁的key
	randomValue int64

	// expire redis过期时间
	expire time.Duration

	mu *Mutex

	// cancelFunc 取消看门狗
	cancelFunc context.CancelFunc

	log util.Logger
}

// Unlock 释放锁
func (h *Holder) Unlock() (int, error) {
	if h == nil {
		return 0, nil
	}
	// 不用传的ctx，防止外层ctx超时或取消，导致锁释放失败
	h.stopWatchDog()
	return h.mu.unlock(context.Background(), h.randomValue)
}

// Start 启动看门狗
func (h *Holder) startWatchDog(ctx context.Context) {
	// 达到过期时间的一半就要检查一次
	var interval time.Duration
	if h.mu.watchDogExpire > h.expire {
		interval = h.expire / 2
	} else {
		interval = h.mu.watchDogExpire / 2
	}
	ticker := time.NewTicker(interval)
	defer ticker.Stop()
	times := 0
	for {
		select {
		case <-ctx.Done():
			// 停止
			return
		case <-ticker.C:
			if h.mu.watchDogMaxTimes > 0 && times > h.mu.watchDogMaxTimes {
				return
			}
			// 续期
			times++
			res := durationScript.Run(ctx, h.mu.client, []string{h.mu.key}, h.randomValue, h.mu.watchDogExpire.Milliseconds())
			if res.Err() != nil {
				if h.log != nil {
					h.log.Errorf("redis续期失败，key: %s, err: %+v", h.mu.key, res.Err())
				} else {
					fmt.Printf("redis续期失败，key: %s, err: %+v \n", h.mu.key, res.Err())
				}
			}
		}

	}
}

// stopWatchDog 停止
func (h *Holder) stopWatchDog() {
	if h.cancelFunc != nil {
		h.cancelFunc()
	}
}
